package com.ft.web.global;


import com.ft.core.exceptions.ApplicationException;
import com.ft.core.exceptions.SystemException;
import com.ft.core.exceptions.UnauthorizedException;
import com.ft.core.exceptions.inner.FanTuException;
import com.ft.core.utils.json.JsonUtils;
import com.ft.web.model.ErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @copyright (C), 2015-2019, 梵途科技有限公司
 * @fileName: GlobalExceptionAdvice
 * @author: 李阳
 * @date: 2019/4/17 10:14
 * @description: 全局异常捕获处理器
 */
@Slf4j
@Controller
public class GlobalExceptionHandlerController implements ErrorController {
    private static final String ERROR_PATH = "/error";
    private ErrorAttributes errorAttributes;

    private static final int accessDeniedCode = 403;
    private static final int unauthorized = 401;

    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }

    @Autowired
    public GlobalExceptionHandlerController(ErrorAttributes errorAttributes) {
        this.errorAttributes = errorAttributes;
    }

    /**
     * web页面错误处理
     */
    @RequestMapping(value = ERROR_PATH, produces = "text/html")
    @ResponseBody
    public String errorPageHandler(HttpServletRequest request, HttpServletResponse response) {
        ErrorResponse errorResponse = buildErrorResponse(request, response);
        return JsonUtils.toJSONString(errorResponse);
    }

    /**
     * 除web页面外的错误处理，比如json/xml等
     */
    @RequestMapping(value = ERROR_PATH)
    @ResponseBody
    public ErrorResponse errorApiHandler(HttpServletRequest request, HttpServletResponse response) {
        return buildErrorResponse(request, response);
    }

    private ErrorResponse buildErrorResponse(HttpServletRequest request, HttpServletResponse response) {
        ServletWebRequest webRequest = new ServletWebRequest(request);
        Throwable throwable = this.errorAttributes.getError(webRequest);
        Map<String, Object> attr = this.errorAttributes.getErrorAttributes(webRequest, false);
        ErrorResponse errorResponse = new ErrorResponse();
        int statusCode = Integer.parseInt(attr.get("status").toString());
        if (throwable instanceof FanTuException) {
            // 自定义异常
            errorResponse.setCode(((FanTuException) throwable).getCode());
            errorResponse.setMsg(throwable.getMessage());
        } else if (throwable instanceof BindException) {
            // 绑定异常
            ApplicationException exception = new ApplicationException(throwable);
            errorResponse.setCode(exception.getCode());
            List<ObjectError> allErrors = ((BindException) throwable).getAllErrors();
            String allErrorsStr = allErrors.stream()
                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.joining(";"));
            errorResponse.setMsg(allErrorsStr);
        } else if (throwable instanceof MethodArgumentNotValidException) {
            // 绑定验证异常
            ApplicationException exception = new ApplicationException(throwable);
            errorResponse.setCode(exception.getCode());
            List<ObjectError> allErrors = ((MethodArgumentNotValidException) throwable).getBindingResult().getAllErrors();
            String allErrorsStr = allErrors.stream()
                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.joining(";"));
            errorResponse.setMsg(allErrorsStr);
        } else if (accessDeniedCode == statusCode || unauthorized == statusCode) {
            // 无权限异常
            UnauthorizedException exception = new UnauthorizedException();
            errorResponse.setCode(exception.getCode());
            errorResponse.setMsg("无权限访问");
        } else {
            // 未定义的异常视为系统异常
            SystemException exception = new SystemException(throwable);
            errorResponse.setCode(exception.getCode());
            errorResponse.setMsg(attr.get("message").toString());
        }
        errorResponse.setPath(attr.get("path").toString());
        setHttpStatusOk(response);

        return errorResponse;
    }

    private void setHttpStatusOk(HttpServletResponse response) {
        response.setStatus(HttpStatus.OK.value());
    }

    /**
     * 全局未定义异常
     *
     * @param e
     * @return
     */
    private ErrorResponse handleGlobalException(Exception e) {
        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setCode(40000);
        errorResponse.setMsg(e.getLocalizedMessage());
        return errorResponse;
    }
}
        
